# -*- coding: utf-8 -*-
# Date: 2020-10-6  Python: 3.7
#
#  清空购物车，再把要购买物品添加到购物车中。配置淘宝帐号、密码、抢购时间，运行程序
#
import datetime
import time
import random
import asyncio

import pyppeteer
import tkinter


class TaobaoEasyBuy:
    """
    类异步
    """
    pyppeteer.DEBUG = True
    browser = None
    page = None

    # 设置登录帐号、密码、抢购开始时间、最大尝试次数、初始购买成功标志
    username = 'username'
    password = 'password'
    buy_time = '2021-02-14 10:08:00.0'
    buy_time_obj = None
    max_buy_times = 1
    is_success = False

    async def screen_size(self):
        # 使用tkinter获取屏幕大小
        tk = tkinter.Tk()
        width = tk.winfo_screenwidth()
        height = tk.winfo_screenheight()
        tk.quit()
        return width, height

    async def _injection_js(self):
        """注入js
        """
        await self.page.evaluateOnNewDocument(
            '() =>{ Object.defineProperties(navigator,' '{ webdriver:{ get: () => false } }) }')  # 本页刷新后值不变

    async def _init(self):
        # 初始化浏览器
        self.browser = await pyppeteer.launch({'headless': False,
                                               'userDataDir': './user_data',
                                               'args': [
                                                   '--window-size=1920,1080',
                                                   '--mute-audio',
                                                   '--no-sandbox',
                                                   '--disable-setuid-sandbox',
                                                   '--disable-gpu',
                                                   '--disable-infobars',
                                                   # '--proxy-server=http://localhost:1080'  # 代理
                                               ],
                                               'dumpio': True,
                                               })
        self.page = await self.browser.newPage()
        # 设置浏览器头部
        await self.page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
                                     '(KHTML, like Gecko) Chrome/71.0.3542.0 Safari/537.36 Edge/16.16299')
        # 设置浏览器视图大小
        width, height = await self.screen_size()
        await self.page.setViewport({'width': width, 'height': height})

    # 获取Cookie
    async def get_cookie(self):
        cookies_list = await self.page.cookies()
        cookies = ''
        for cookie in cookies_list:
            str_cookie = '{0}={1};'
            str_cookie = str_cookie.format(cookie.get('name'), cookie.get('value'))
            cookies += str_cookie
        print(cookies)
        return cookies

    # 登录滑块
    async def mouse_slider(self):
        """滑动滑块
        """
        await asyncio.sleep(3)
        try:
            await self.page.hover('#nc_1_n1z')
            # 鼠标按下按钮
            await self.page.mouse.down()
            # 移动鼠标
            await self.page.mouse.move(2000, 0, {'steps': 30})
            # 松开鼠标
            await self.page.mouse.up()
            await asyncio.sleep(2)
        except Exception as e:
            print(e, '      :错误')
            return None
        else:
            await asyncio.sleep(3)
            # 获取元素内容
            slider_again = await self.page.querySelectorEval('#nc_1__scale_text', 'node => node.textContent')
            if slider_again != '验证通过……':
                return None
            else:
                print('验证通过……')
                return True

    async def start(self):
        # 检查抢购时间
        # 初始化参数
        self.buy_time_obj = int(time.mktime(time.strptime(self.buy_time, '%Y-%m-%d %H:%M:%S.%f')) * 1000)
        # now_time = int(time.time() * 1000)
        # if now_time > self.buy_time_obj:
        #     print(f'{await self.get_now()} 当前已过抢购时间，请确认抢购时间是否填错……')
        #     await self.exit()

        """登陆
        """
        # 初始化浏览器
        await self._init()
        # 注入js
        await self._injection_js()
        # 打开淘宝登陆页面
        await self.page.goto('https://login.taobao.com')
        print(f'{await self.get_now()} 打开登录页,自动输入帐号密码……')

        # 点击密码登陆按钮
        await self.page.click('a.password-login-tab-item')
        time.sleep(random.random() * 2)
        # 输入用户名
        await self.page.type('#fm-login-id', self.username, {'delay': random.randint(100, 151) - 50})
        # 输入密码
        await self.page.type('#fm-login-password', self.password, {'delay': random.randint(100, 151)})
        time.sleep(random.random() * 2)
        # 获取滑块元素
        try:
            slider = await self.page.Jeval('span.nc_iconfont btn_slide', 'node => node.style')
        except:
            slider = None

        if slider:
            print('有滑块')
            # 移动滑块
            flag = await self.mouse_slider()
            if not flag:
                print('滑动滑块失败')
                return None
            time.sleep(random.random() + 1.5)

        # 点击登陆
        print(f'{await self.get_now()} 点击登录按钮……')
        await asyncio.wait([
            self.page.waitForNavigation(),
            self.page.click('button.password-login'),
        ], timeout=2)
        print(f'{await self.get_now()} 登录成功……')
        # cookies = await self.get_cookie()

        await asyncio.wait([
            self.page.waitForNavigation(),
            self.page.click('a#mc-menu-hd'),
        ], timeout=2)
        print(f'{await self.get_now()} 进入购物车页面……')

        # 保持登录
        while True:
            now_time = int(time.time() * 1000)
            # 距离开始3分钟，定时刷新页面避免帐号退出
            if self.buy_time_obj - now_time > (60 * 3 * 1000):
                await self.keep_alive()
            else:
                print("抢购时间点将近，停止自动刷新，准备进入抢购阶段……")
                break

        # 开始抢购
        await self.do_buy()

    # 抢购
    async def do_buy(self):
        # 初始化购买次数
        buy_times = 0
        # 在购物车页面循环下单，直到抢购成功或者到达最大尝试购买次数
        while True:
            # 验证是否陈功
            if self.is_success is True:
                print(f'{await self.get_now()} 抢购成功!……')
                break
            else:
                if buy_times >= self.max_buy_times:
                    print(f'{await self.get_now()} 抢购失败，到达尝试次数!……')
                    break
                else:
                    try:
                        # 等待到达开始抢购时间
                        now_time = int(time.time() * 1000)
                        while now_time < self.buy_time_obj:
                            # 如果未到达购买时间 则休息0.1秒
                            if (int(now_time / 1000) % 5) == 0:
                                now = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                                print(f'{await self.get_now()} 等待开始抢购……')
                            await asyncio.sleep(0.1)
                            now_time = int(time.time() * 1000)

                        print("===================================================================================")
                        buy_times += 1
                        print(f'{await self.get_now()} 到达开始抢购时间，开始抢购……第{buy_times}次')

                        # 1.点击全选
                        while True:
                            try:
                                await self.page.click('div#J_SelectAll1')
                                print(f'{await self.get_now()} 点击全选按钮……')
                                break
                            except:
                                await asyncio.sleep(0.01)

                        # 2.点击结算
                        while True:
                            try:
                                status = await self.page.Jeval('a#J_Go', 'el=>el.getAttribute("class")')
                                # print(status)
                                if 'submit-btn-disabled' not in status:
                                    await self.page.click('a#J_Go')
                                    print(f'{await self.get_now()} 点击结算按钮……')
                                    break
                            except:
                                await asyncio.sleep(0.01)

                        # 3.点击下订单
                        curr_buy_times = 0
                        while curr_buy_times < 200:
                            try:
                                # 验证页面是否存在提交订单按钮
                                await self.page.click('a.go-btn')
                                self.is_success = True
                                print(f'{await self.get_now()} 点击下订单按钮成功……')
                                # 等待跳转成功后自动跳转到付款页面，停的太早无法执行下订单
                                print(f'{await self.get_now()} 等待下订单流程自动跳转付款页面……')
                                await asyncio.sleep(60)
                                break
                            except:
                                curr_buy_times += 1
                                print(f'{await self.get_now()} 未获取到下订单按钮……失败{curr_buy_times}次')
                                await asyncio.sleep(0.1)

                        # 4.下单失败重新进购物车页面
                        if self.is_success is not True:
                            while True:
                                try:
                                    await asyncio.wait([
                                        self.page.waitForNavigation(),
                                        self.page.click('a#mc-menu-hd'),
                                    ], timeout=1)
                                    print(f'{await self.get_now()} 重新进入购物车页面……')
                                    break
                                except:
                                    await asyncio.sleep(0.1)
                    except Exception as e:
                        print(e)
                        print(f'{await self.get_now()} 抢购程序异常退出……')

        # 退出
        await self.exit()

    async def get_now(self):
        return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")

    async def keep_alive(self):
        print(f'{await self.get_now()} 刷新页面……')
        await self.page.reload()
        await asyncio.sleep(60)

    async def exit(self):
        # 退出
        print('执行完成退出！')
        if self.browser is not None:
            await self.browser.close()
        exit(0)


if __name__ == '__main__':
    easy_buy = TaobaoEasyBuy()
    loop = asyncio.get_event_loop()
    task = asyncio.ensure_future(easy_buy.start())
    loop.run_until_complete(task)
